from flask import current_app
from flask import g, jsonify
from flask import render_template
from flask import request
from flask import session
from werkzeug.utils import redirect

from info import constants
from info import db
from info.models import Category, News
from info.models import User
from info.modules import news
from info.utils.commons import user_login_data
from info.utils.image_storage import image_storage
from info.utils.response_code import RET
from . import profile_blue


###################################################################################

# 请求路径: /user/info
# 请求方式:GET
# 请求参数:无
# 返回值: user.html页面,用户字典data数据
@profile_blue.route('/info')
@user_login_data
def user_info():
    """
    获取用户信息
    1. 获取到当前登录的用户模型
    2. 返回模型中指定内容
    :return:
    """
    # 1. 获取到当前登录的用户模型
    user = g.user

    if not user:
        return redirect("/")

    data = {
        "user_info": user.to_dict()
    }
    # 2. 返回模型中指定内容
    return render_template("news/user.html", data=data)


###################################################################################


# 功能描述:用户基本信息展示
# 请求路径: /user/base_info
# 请求方式:GET,POST
# 请求参数:POST请求有参数,nick_name,signature,gender
# 返回值:errno,errmsg

@profile_blue.route('/base_info', methods=["GET", "POST"])
@user_login_data
def base_info():
    """
    用户基本信息
    1. 获取用户登录信息
    2. 获取到传入参数
    3. 更新并保存数据
    4. 返回结果
    :return:
    """
    # 1.获取用户登陆状态
    user = g.user

    # 因为在进入个人中心的视图函数中就已经进行了用户状态的判断,所以此时就不需要再次判断了
    if request.method == "GET":
        return render_template("news/user_base_info.html", data={"user_info": user.to_dict()})

    # 2.获取到传入参数,并且进行校验
    nick_name = request.json.get("nick_name")
    gender = request.json.get("gender")
    signature = request.json.get("signature")

    # 2.1.对参数进行校验
    if not all([nick_name, gender, signature]):
        return jsonify(errno=RET.DATAERR, errmsg="个人中心:获取参数有误.")

    if gender not in (["MAN", "WOMAN"]):
        return jsonify(errno=RET.DATAERR, errmsg="个人中心:获取性别参数有误")

    # 3.更新并保存数据
    user.nick_name = nick_name
    user.gender = gender
    user.signature = signature

    # 3.1.保存数据
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg="个人中心:保存用户信息有误")

    # 3.2.将 session 中的数据进行及时更新
    session['nick_name'] = nick_name

    # 4.返回更新后的结果
    return jsonify(errno=RET.OK, errmsg="个人中心:数据更新成功")


###################################################################################


# 功能描述: 图片上传
# 请求路径: /user/pic_info
# 请求方式:GET,POST
# 请求参数:无, POST有参数,avatar
# 返回值:GET请求: user_pci_info.html页面,data字典数据, POST请求: errno, errmsg,avatar_url

@profile_blue.route('/pic_info', methods=["GET", "POST"])
@user_login_data
def pic_info():
    """
    1.获取到用户上传的文件
    2.将获取到的文件上传到七牛云
    3.将头像信息更新到当前用户的模型中
    3.1.设置用户模型相关数据
    3.2.将数据保存到数据库
    4.返回上传的结果<avatar_url>
    :return:
    """

    user = g.user

    # 判断请求方式:
    if request.method == "GET":
        return render_template('news/user_pic_info.html', users=user.to_dict())

    # 1.获取到用户上传的文件
    try:
        avatar_file = request.files.get("avatar")

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="读取用户上传文件出错")

    # 2.将获取到的文件上传到七牛云
    try:
        image_name = image_storage(avatar_file.read())

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="上传图片到七牛云失败")

    # 2.1.判断上传的头像是否有数据
    if not image_name: return jsonify(errno=RET.NODATA, errmsg="图片上传七牛云失败")

    # 3.将头像信息更新到当前用户的模型中
    user.avatar_url = image_name

    # 3.1.将数据保存到数据库
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="用户头像数据保存到数据库失败")

    # 4.返回响应,返回图片地址
    data = {
        "avatar_url": constants.QINIU_DOMIN_PREFIX + image_name
    }
    return jsonify(errno=RET.OK, errmsg="上传成功", data=data)


###################################################################################


# 功能描述: 修改密码
# 请求路径: /user/pass_info
# 请求方式:GET,POST
# 请求参数:GET无, POST有参数,old_password, new_password
# 返回值:GET请求: user_pass_info.html页面,data字典数据, POST请求: errno, errmsg

@profile_blue.route('/pass_info', methods=['GET', 'POST'])
@user_login_data
def pass_info():
    # 判断请求方式,返回页面
    if request.method == "GET":
        return render_template("news/user_pass_info.html")

    # 1.获取传入的参数
    old_password = request.json.get("old_password")
    new_password = request.json.get("new_password")

    # 2.对传入的参数进行校验
    if not all([old_password, new_password]):
        return jsonify(errno=RET.PARAMERR, errmsg="修改密码参数有误")

    # 3.获取当前用户的登陆信息,并进行密码的判断
    user = g.user

    if not user.check_password(old_password):
        return jsonify(errno=RET.PWDERR, errmsg="原密码错误")

    # 4.更新数据到数据库
    user.password = new_password

    # 修改密码之后清空用户信息.(退出登陆)
    session.pop("user_id", None)
    session.pop("nick_name", None)
    session.pop("mobile", None)

    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="更新密码到数据库出现错误")

    # 5.数据保存成功返回状态
    return jsonify(errno=RET.OK, errmsg="保存新密码到数据库成功")


###################################################################################


# 功能描述: 获取我的收藏
# 请求路径: /user/ collection
# 请求方式:GET
# 请求参数:p(页数)
# 返回值: user_collection.html页面

@profile_blue.route('/user_collection')
@user_login_data
def user_collection():
    # 获取页数
    p = request.args.get("p", 1)

    try:
        p = int(p)
    except Exception as e:
        current_app.logger.error(e)
        p = 1

    # 获取用户状态,并设置初始值
    user = g.user
    collections = []
    current_page = 1
    total_page = 1

    try:
        # 进行分页查询数据
        paginate = user.collection_news.paginate(p, constants.USER_COLLECTION_MAX_NEWS, False)

        # 获取分页数据
        collections = paginate.items

        # 获取当前页
        current_page = paginate.page

        # 获取总页数
        total_page = paginate.pages

    except Exception as e:
        current_app.logger.error(e)

    # 收藏列表
    collection_dict_li = []

    for new in collections:
        collection_dict_li.append(new.to_dict())

    data = {
        "total_page": total_page,
        "current_page": current_page,
        "collections": collection_dict_li
    }

    return render_template("news/user_collection.html", data=data)


###################################################################################


# 功能描述: 新闻发布
# 请求路径: /user/news_release
# 请求方式:GET,POST
# 请求参数:GET无, POST ,title, category_id,digest,index_image,content
# 返回值:GET请求,user_news_release.html, data分类列表字段数据, POST,errno,errmsg

@profile_blue.route('/news_release', methods=['GET', 'POST'])
@user_login_data
def news_release():
    # 1.如果是 GET 请求,携带分类渲染主页.
    if request.method == "GET":

        # 查询数据库信息
        try:
            categories = Category.query.all()
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg="新闻发布:查询分类信息失败")

        # 将查询的信息转换成字典列表
        category_list = []
        for categorie in categories:
            category_list.append(categorie.to_dict())

        return render_template("news/user_news_release.html", categories=category_list)

    # 2.如果是 POST 请求,则获取参数
    title = request.form.get("title")
    category_id = request.form.get("category_id")
    digest = request.form.get("digest")
    index_image = request.files.get("index_image")
    content = request.form.get("content")

    # 3.校验参数
    if not all([title, category_id, digest, index_image, content]):
        return jsonify(errno=RET.PARAMERR, errmsg="新闻发布:参数不全")

    # 4.上传图片,并判断是否上传成功
    try:
        image_name = image_storage(index_image.read())
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="新闻发布:上传七牛云失败")

    # 判断是否上传成功
    if not image_name: return jsonify(errno=RET.NODATA, errmsg="新闻发布:上传图片失败")

    # 5.创建新闻对象
    news = News()
    news.title = title
    news.source = g.user.nick_name
    news.digest = digest
    news.content = content
    news.index_image_url = constants.QINIU_DOMIN_PREFIX + image_name
    news.category_id = category_id
    news.user_id = g.user.id
    news.status = 1

    # 6.保存新闻数据到数据库
    try:
        db.session.add(news)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="新闻发布:保存新闻到数据库发生异常")

    # 7.返回响应
    return jsonify(errno=RET.OK, errmsg="恭喜您:新闻发布成功")


###################################################################################


# 功能描述: 获取发布的新闻列表
# 请求路径: /user/news_list
# 请求方式:GET
# 请求参数:p
# 返回值:GET渲染user_news_list.html页面
@profile_blue.route('/news_list')
@user_login_data
def news_list():
    # 1.获取参数
    page = request.args.get("p", "1")

    # 2.参数转换
    try:
        page = int(page)
    except Exception as e:
        page = 1

    # 3.分页查询
    try:
        paginate = News.query.filter(News.user_id == g.user.id).order_by(News.create_time.desc()).paginate(page, 3,
                                                                                                           False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取新闻列表失败")

    # 4.获取分页对象属性,总页数,当前页,对象列表
    totalPage = paginate.pages
    currentPage = paginate.page
    items = paginate.items

    # 5.对象列表转成字典列表
    news_list = []
    for item in items:
        news_list.append(item.to_review_dict())

    # 6.拼接数据,渲染界面
    data = {
        "totalPage": totalPage,
        "currentPage": currentPage,
        "news_list": news_list
    }

    # 7. return 返回
    return render_template("news/user_news_list.html", data=data)


###################################################################################

# 功能描述: 获取作者详情
# 请求路径: /user/other
# 请求方式: GET
# 请求参数:id
# 返回值: 渲染other.html页面,字典data数据
@profile_blue.route('/other')
@user_login_data
def other():
    """
    - 1.获取参数
    - 2.为空校验
    - 3.根据编号取出作者对象,并判断作者对象是否存在
    - 4.返回信息,渲染界面
    :return:
    """
    # - 1.获取参数
    author_id = request.args.get("id")

    # - 2.为空校验
    if not author_id:
        return jsonify(errno=RET.PARAMERR, errmsg="参数不全")

    # - 3.根据编号取出作者对象,并判断作者对象是否存在
    try:
        author = User.query.get(author_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取作者失败")

    # -3.1.判断作者是否存在
    if not author: return jsonify(errno=RET.NODATA, errmsg="作者不存在")

    # -3.2.判断用户是否关注过该新闻的作者
    is_followed = False
    # 判断用户是否登陆判断该新闻是否有作者
    if g.user:
        # 判断该用户是否在新闻作者的粉丝列表中
        if g.user in author.followers:
            is_followed = True

    data = {
        "author": author.to_dict(),
        "user_info": g.user.to_dict() if g.user else "",
        "is_followed": is_followed
    }

    # - 4.返回信息,渲染界面
    return render_template("news/other.html", data=data)


###################################################################################


# 功能描述: 作者新闻列表获取
# 请求路径: /user/other_news_list
# 请求方式: GET
# 请求参数:p,user_id
# 返回值: errno,errmsg
@profile_blue.route('/other_news_list')
def other_news_list():
    """
    -1.获取参数
    -2.参数为空校验,参数类型转换
    -3.根据作者编号取出作者对象,并判断作者是否存在
    -4.根据作者对象取出分页数据
    -5.根据分页属性取出,总页数,当前页,当前页列表
    -6.对象列表转字典列表
    -7.携带数据返回
    :return:
    """
    # -1.获取参数
    page = request.args.get("p", "1")
    author_id = request.args.get("user_id")

    # -2.参数为空校验,参数类型转换
    if not all([author_id]):
        return jsonify(errno=RET.NODATA, errmsg="作者新闻列表获取:参数不全")

    try:
        page = int(page)
    except Exception as e:
        page = 1

    # -3.根据作者编号取出作者对象,并判断作者是否存在
    try:
        author = User.query.get(author_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="作者新闻列表获取:获取作者对象失败")

    if not author: return jsonify(errno=RET.NODATA, errmsg="作者对象不存在")

    # -4.根据作者对象取出分页数据
    try:
        # paginate = News.query.filter(News.user_id == author_id).order_by(News.create_time.desc()).paginate(page,3,False)
        paginate = author.news_list.order_by(News.create_time.desc()).paginate(page, 3, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取新闻分页数据失败")

    # 5.获取分页对象属性,总页数,当前页,当前页对象列表
    total_page = paginate.pages
    current_page = paginate.page
    items = paginate.items

    # 6.对象列表转字典列表
    news_list = []
    for item in items:
        news_list.append(item.to_dict())

    # 7.携带数据,返回响应
    data = {
        "total_page": total_page,
        "current_page": current_page,
        "news_list": news_list
    }
    return jsonify(errno=RET.OK, errmsg="获取数据成功", data=data)


###################################################################################


# 功能: 获取我的关注
# 请求路径: /user/user_follow
# 请求方式: GET
# 请求参数:p
# 返回值: 渲染user_follow.html页面,字典data数据
@profile_blue.route('/user_follow')
@user_login_data
def user_follow():
    """
    -1.获取参数
    -2.参数类型转换
    -3.分页查询
    -4.获取分页属性,总页数,当前页,当前页对象列表
    -5.用户对象列表转字典
    -6.携带数据渲染页面
    :return:
    """
    # -1.获取参数
    page = request.args.get("p", "1")

    # -2.参数类型转换
    try:
        page = int(1)
    except Exception as e:
        page = 1

    # -3.分页查询
    try:
        paginate = g.user.followed.paginate(page, 2, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取分页数据失败")

    # -4.获取分页属性,总页数,当前页,当前页对象列表
    totalPage = paginate.pages
    currentPage = paginate.page
    items = paginate.items

    # -5.用户对象列表转字典
    users_list = []
    for item in items:
        users_list.append(item.to_dict())

    # -6.携带数据渲染页面
    data = {
        "totalPage": totalPage,
        "currentPage": currentPage,
        "users_list": users_list
    }
    return render_template("news/user_follow.html", data=data)